大家好,歡迎來到 FastAPI 系列文章的第 6 天!今天我們要探討另一種平行處理的方式:Thread(執行緒) 和 Process(程序)。
有些地方會把 Thread 翻譯成「線程」,把 Process 翻譯成「進程」。
在 Python 的世界裡,我們有三種主要的並發處理方式:
每種方式都有其獨特的適用場景,理解它們的差異和適用時機是構建高效能 FastAPI 應用的關鍵。
執行緒 是作業系統中最小的執行單位,多個執行緒可以在同一個程序中並行執行。在 Python 中,執行緒特別適合處理 I/O 密集型任務,因為當一個執行緒在等待 I/O 操作時,其他執行緒可以繼續工作。
執行緒最適合以下情況:
在 Python 中,我們使用 threading 模組來操作執行緒:
import threading
import time
import os
def worker(worker_id):
    # os.getpid() 取得行程ID,threading.get_ident() 取得執行緒ID
    print(f"Worker {worker_id} - Process ID: {os.getpid()}, Thread ID: {threading.get_ident()} - 開始工作")
    time.sleep(2)
    print(f"Worker {worker_id} - Process ID: {os.getpid()}, Thread ID: {threading.get_ident()} - 完成工作")
if __name__ == "__main__":
    print("主執行緒開始")
    
    # 建立兩個執行緒
    t1 = threading.Thread(target=worker, args=(1,))
    t2 = threading.Thread(target=worker, args=(2,))
    
    # 啟動執行緒
    t1.start()
    t2.start()
    
    print("主執行緒繼續執行...")
    
    # 等待所有子執行緒結束
    t1.join()
    t2.join()
    
    print("主執行緒結束")

你會發現,兩個執行緒的 Process ID 都跟主程式是一樣的,但 Thread ID 會不同。
執行緒的優勢在於它能夠有效利用 I/O 等待時間。當一個執行緒在等待網路回應時,CPU 可以切換到其他執行緒繼續工作,從而提高整體效能。
程序 是作業系統中資源分配的基本單位,每個程序都有獨立的記憶體空間。程序間的通訊比執行緒複雜,但它們可以真正並行執行,不受 Python GIL 的限制。
程序最適合以下情況:
import multiprocessing
import time
import os
def worker(worker_id):
    # os.getpid() 可以取得當前行程的 ID
    print(f"Worker {worker_id} - Process ID: {os.getpid()} - 開始工作")
    time.sleep(2)
    print(f"Worker {worker_id} - Process ID: {os.getpid()} - 完成工作")
if __name__ == "__main__":
    print(f"主行程 - Process ID: {os.getpid()} - 開始")
    
    # 建立兩個程序
    p1 = multiprocessing.Process(target=worker, args=(1,))
    p2 = multiprocessing.Process(target=worker, args=(2,))
    
    # 啟動程序
    p1.start()
    p2.start()
    
    print("主行程繼續執行...")
    
    # 等待所有子程序結束
    p1.join()
    p2.join()
    
    print("主行程結束")

程序的優勢在於它們可以充分利用多核心 CPU,實現真正的平行計算,不受 Python GIL 的限制。你會看到兩個子程序都有不同的 Process ID,它們是完全獨立運行的。
這是一個經典問題,答案取決於你的「任務類型」:
Thread 或 Asyncio。當一個執行緒在等待 I/O 時,Python 會把它切換掉,讓 CPU 去執行其他執行緒,從而提升效率。Process。因為 GIL 的存在,多執行緒對 CPU 密集任務完全沒幫助。必須使用多行程,才能將任務分配到不同 CPU 核心上,實現真正的平行加速。Thread 和 Process 為 FastAPI 提供了處理不同類型工作負載的強大能力。執行緒適合 I/O 密集型任務,程序適合 CPU 密集型任務,而 Asyncio 則適合高併發的 I/O 處理。
理解這三種並發模式的特性和適用場景,能夠幫助我們構建既高效又穩定的 FastAPI 應用。在實際開發中,最好的策略往往是根據具體需求靈活組合使用這些技術。
明天我們將探討 GIL(Global Interpreter Lock),深入了解為什麼在某些情況下執行緒的效能會受到限制,以及如何正確應對這個挑戰!
並發真的不好處理,學習了
有可能會提到 celery worker 這種嗎?
或是之後當補充番外篇
有~ 我在第 17、18 篇有提到 celery worker,包含一些簡單的範例